﻿using MyNes.Core;
using neside3.ext;
using SDL2;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace runner
{
    /// <summary>
    /// wpf_nesview.xaml 的交互逻辑
    /// </summary>
    public partial class wpf_nesview : UserControl
    {
        public wpf_nesview()
        {
            InitializeComponent();
        }
        ILogger logger;
        public wpf_nesview(ILogger logger) : base()
        {
            this.logger = logger;
        }
        public bool SetProject(string projectfolder)
        {
            var projectName = System.IO.Path.GetFileName(projectfolder);
            var nespath = System.IO.Path.Combine("temp", projectName, projectName + ".nes");
            if (System.IO.File.Exists(nespath))
            {
                MyNes.Core.NesEmu.LoadGame(nespath, out bool success, true);

                return success;

            }
            return false;
        }
        const ushort buffersize = 1024 * 5;
        byte[] audiobuf = new byte[buffersize * 20];
        int audioreadpos = 0;
        int audiowritepos = 0;
        int samples_added = 0;
        WriteableBitmap bitmap;
        int[] imagebuf;

        DispatcherTimer dispatcherTimer;

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            dispatcherTimer = new DispatcherTimer();
            dispatcherTimer.Tick += (s, ee) => CurThread_UpdateBitmap();
            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
            //init bitmap
            bitmap = new WriteableBitmap(256, 240, 96, 96, PixelFormats.Bgra32, null);
            this.rootImg.Source = bitmap;
            this.imagebuf = new int[256 * 240];
            //init audio
            InitAudio();
            //init emu
            InitEmu();
        }

        private unsafe void InitEmu()
        {
            Controller con = new Controller();
            MyNes.Core.MyNesMain.Light_Init();
            NesEmu.SetupControllersP1(con);
            MyNes.Core.NesEmu.Light_SetupVideoRender((ref int[] datas) =>
            {
                fixed (int* bs = datas)
                {
                    fixed (int* dest = imagebuf)
                    {
                        for (var i = 0; i < datas.Length; i++)
                        {
                            dest[i] = bs[i];
                        }
                    }
                }
            });
            MyNes.Core.NesEmu.Light_SetupAudioSamples((ref short[] buffer, ref int samples_count) =>
            {
                var buflen = audiobuf.Length;
                for (var i = 0; i < samples_count; i++)
                {
                    var sample = (short)(buffer[i] * 0.5f);
                    //var sb = BitConverter.GetBytes(sample);
                    var hb = (byte)(sample >> 8); ;
                    var lb = (byte)(sample & 0xFF);
                    //明明说是小頭順序，这里sample 的 高位 为什么放在前面？
                    //这不是和系统顺序反了吗
                    if (BitConverter.IsLittleEndian)
                    {
                        audiobuf[audiowritepos++ % buflen] = hb;
                        audiobuf[audiowritepos++ % buflen] = lb;
                    }
                    else
                    {
                        audiobuf[audiowritepos++ % buflen] = lb;
                        audiobuf[audiowritepos++ % buflen] = hb;
                    }
                    samples_added++;
                }
            });
        }

        private unsafe void CurThread_UpdateBitmap()
        {
            bitmap.Lock();
            fixed (void* bs = imagebuf)
            {
                IntPtr buffer = new IntPtr(bs);
                bitmap.WritePixels(new System.Windows.Int32Rect(0, 0, 256, 240), buffer, 256 * 240, 256);
            }
            bitmap.Unlock();
        }
        private void InitAudio()
        {
            var result = SDL.SDL_Init(SDL.SDL_INIT_AUDIO);
            logger.Log("init sdl audio=" + result);
            SDL.SDL_AudioSpec wanted_spec = new SDL.SDL_AudioSpec();
            wanted_spec.freq = 48000;
            wanted_spec.format = SDL.AUDIO_S16SYS;//根据系统决定合适的字节序
            wanted_spec.channels = 1;
            wanted_spec.silence = 0;
            wanted_spec.samples = buffersize;
            wanted_spec.callback = SDL_AudioCallback;
            try
            {
                var resultopen = SDL.SDL_OpenAudio(ref wanted_spec, IntPtr.Zero);
                Console.WriteLine("open sdl audio=" + resultopen);
                if (resultopen < 0)
                {
                    var strerr = SDL.SDL_GetError();
                    Console.WriteLine("open sdl audio=" + strerr);

                }
                SDL.SDL_PauseAudio(0);
            }
            catch (Exception err)
            {
                logger.Error("init sdl audio=SDL_OpenAudio error" + result);
            }
        }

        bool start = false;
        unsafe void SDL_AudioCallback(IntPtr userdata, IntPtr stream, int len)
        {
            if (!start)
            {
                if (samples_added > 3000)
                    start = true;
                return;
            }
            var buflen = audiobuf.Length;
            byte* data = (byte*)stream;
            for (var i = 0; i < len; i += 2)
            {
                if (samples_added > 0)
                {
                    data[i] = audiobuf[audioreadpos++ % buflen];
                    data[i + 1] = audiobuf[audioreadpos++ % buflen];
                    samples_added--;
                }
            }
        }

        class Controller : MyNes.Core.IJoypadConnecter
        {
            public bool bLeft;
            public bool bUp;
            public bool bRight;
            public bool bDown;
            public bool bBtnA;
            public bool bBtnB;
            public bool bBtnSelect;
            public bool bBtnStart;
            public override void Update()
            {
                var tagright = (bRight ? 1 : 0) << 7; //right
                var tagleft = (bLeft ? 1 : 0) << 6;//left
                var tagdown = (bDown ? 1 : 0) << 5;
                var tagup = (bUp ? 1 : 0) << 4;
                var tagStart = (bBtnStart ? 1 : 0) << 3; //sure
                var tagSel = (bBtnSelect ? 1 : 0) << 2;
                var tagB = (bBtnB ? 1 : 0) << 1;
                var tagA = (bBtnA ? 1 : 0) << 0;
                this.DATA = (byte)(tagleft | tagup | tagdown | tagright | tagA | tagB | tagSel | tagStart);
            }
        }
    }
}
